#pragma once

#include <Functor.h>
#include <Packet.h>

class CLocalPlayer;
class CGamePlayer;
class CGameObject;
class CPlanet;
class CTorp;
class CPlasma;
class CPhaser;
class CGalaxyStatus;
enum EServerPacketType;
struct packet_base;
class CNetrekClientListener;
enum EShipType;
class CShipData;
enum ETeamID;

typedef vector<CGamePlayer*> CPlayerList;
typedef vector<CPlanet*> CPlanetList;
typedef vector<CTorp*> CTorpList;
typedef vector<CPlasma*> CPlasmaList;
typedef vector<CPhaser*> CPhaserList;
typedef vector<CShipData*> CShipList;

typedef list<mesg_spacket> CChatHistory;


enum EGameWorldEventID
{
    eGameWorldEventID_PlayerStatsUpdated = 1,
    eGameWorldEventID_Chat,
    eGameWorldEventID_PlayerIsAlive,
    eGameWorldEventID_PlayerIsDead,
    eGameWorldEventID_PlayerLocationUpdated,
    eGameWorldEventID_ShowPlanetDetail,
    eGameWorldEventID_TorpFired,
    eGameWorldEventID_PlasmaFired,
    eGameWorldEventID_PhaserFired,
    eGameWorldEventID_PlayerCloaked,
    eGameWorldEventID_PlayerDecloaked,
    eGameWorldEventID_TorpHit,
    eGameWorldEventID_PlasmaHit,
    eGameWorldEventID_TractorBegin,
    eGameWorldEventID_TractorEnd,
    eGameWorldEventID_PlayerExplodes,
    eGameWorldEventID_PlanetNameLocationChanged,
    eGameWorldEventID_PlanetResourceUpdated,
    eGameWorldEventID_BeginFrameUpdate,
    eGameWorldEventID_EndFrameUpdate
};

class CGameWorldEvent
{
public:
    CGameWorldEvent(EGameWorldEventID eEventID) : m_eEventID(eEventID) { }
    EGameWorldEventID GetEventID() { return m_eEventID; }

private:
    EGameWorldEventID m_eEventID;
};

class CPlayerEvent : public CGameWorldEvent
{
public:
    CPlayerEvent(CGamePlayer* pGamePlayer, EGameWorldEventID eEventID) : CGameWorldEvent(eEventID), m_pGamePlayer(pGamePlayer) { }
    CGamePlayer* GetPlayer() { return m_pGamePlayer; }
private:
    CGamePlayer* m_pGamePlayer;
};

class CTorpEvent : public CGameWorldEvent
{
public:
    CTorpEvent(CTorp* pTorp, EGameWorldEventID eEventID) : CGameWorldEvent(eEventID), m_pTorp(pTorp) { }
    CTorp* GetTorp() { return m_pTorp; }
private:
    CTorp* m_pTorp;
};

class CPlasmaEvent : public CGameWorldEvent
{
public:
    CPlasmaEvent(CPlasma* pPlasma, EGameWorldEventID eEventID) : CGameWorldEvent(eEventID), m_pPlasma(pPlasma) { }
    CPlasma* GetPlasma() { return m_pPlasma; }
private:
    CPlasma* m_pPlasma;
};

class CPhaserEvent : public CGameWorldEvent
{
public:
    CPhaserEvent(CPhaser* pPhaser, EGameWorldEventID eEventID) : CGameWorldEvent(eEventID), m_pPhaser(pPhaser) { }
    CPhaser* GetPhaser() { return m_pPhaser; }
private:
    CPhaser* m_pPhaser;
};



class CPlanetEvent : public CGameWorldEvent
{
public:
    CPlanetEvent(CPlanet* pPlanet, EGameWorldEventID eEventID) : CGameWorldEvent(eEventID), m_pPlanet(pPlanet) { }
    CPlanet* GetPlanet() { return m_pPlanet; }
private:
    CPlanet* m_pPlanet;
};


class CChatEvent : public CGameWorldEvent
{
public:
    CChatEvent(const mesg_spacket* pMessage) : CGameWorldEvent(eGameWorldEventID_Chat), m_pMessage(pMessage) { }
    const mesg_spacket* GetMessage() { return m_pMessage; }
private:
    const mesg_spacket* m_pMessage;
};


class CGameWorld
{
public:
    CGameWorld(CNetrekClientListener*);
    ~CGameWorld();
    void Reset();

    void FrameUpdate();

    void ProcessPacket(EServerPacketType ePacketType, const packet_base* pPacket);

    CNetrekClientListener* GetListener() { return m_pListener; }

    CGalaxyStatus* GetGalaxyStatus();
    CLocalPlayer* GetLocalPlayer();
    CGamePlayer* GetPlayerForLocalPlayer();

    CPlayerList& GetPlayerList() { return m_rgpPlayers; };
    CGamePlayer* GetPlayerByID(DWORD dwPlayerID);

    CPlanetList& GetPlanetList() { return m_rgpPlanets; };
    CPlanet* GetPlanetByID(DWORD dwPlanetID);
    CPlanet* GetPlanetByLocation(DWORD dwXLocation, DWORD dwYLocation, float flTolerance);

    CTorp* GetTorpByID(DWORD dwTorpID);
    CPlasma* GetPlasmaByID(DWORD dwPlasmaID);
    CPhaser* GetPhaserByID(DWORD dwPhaserID);

    CShipData* GetShipByType(EShipType eShipType);

    CChatHistory& GetChatHistory() { return m_rgChatMessages; }

    Ogre::SceneManager* GetSceneManager();

    typedef FunctorTemplate<void, CGameWorldEvent*> EventSubscriber;
    class CEventSubscription
    {
    public:
        CEventSubscription(const EventSubscriber& eventSubscriber) : m_eventSubscriber(eventSubscriber) { };
        const EventSubscriber& GetEventSubscriber() { return m_eventSubscriber; }
    private:
        EventSubscriber m_eventSubscriber;
    };

    CEventSubscription* SubscribeEvents(const EventSubscriber& eventSubscriber);
    void UnsubscribeEvents(CEventSubscription* pSubscription);

    void FireEvent(CGameWorldEvent* pGameWorldEvent);
    void FireSimpleEvent(EGameWorldEventID);

    float GetTimeSinceLastFrameAsFloat() const;

    const char* GetShipMeshFileName(ETeamID eTeamID, EShipType eShipType) const;

    Ogre::BillboardSet* GetTorpBillboardSet() { return m_pTorpBillboardSet; }
    Ogre::BillboardSet* GetPlasmaBillboardSet() { return m_pPlasmaBillboardSet; }
    Ogre::BillboardSet* GetPhaserBillboardSet() { return m_pPhaserBillboardSet; }

private:


    typedef vector<EventSubscriber> EventSubscriberList;
    EventSubscriberList m_rgEventSubscribers;

    auto_ptr<CLocalPlayer> m_pLocalPlayer;
    auto_ptr<CGalaxyStatus> m_pGalaxyStatus;

    CPlayerList m_rgpPlayers;
    CPlanetList m_rgpPlanets;

    CTorpList m_rgpTorps;
    CPlasmaList m_rgpPlasmas;
    CPhaserList m_rgpPhasers;
    CShipList m_rgpShips;

    CChatHistory m_rgChatMessages;
    static const int c_maxChatMessages = 200;

    string  m_strMOTDMessage;

    CNetrekClientListener* m_pListener;

    Ogre::MeshPtr m_pGridPlaneMesh;
    Ogre::Entity* m_pGridPlaneEntity;
    Ogre::SceneNode* m_pGridPlaneNode;

    Ogre::Light* m_pLight;
    Ogre::SceneNode* m_pLightNode;

    Ogre::BillboardSet* m_pTorpBillboardSet;
    Ogre::SceneNode* m_pTorpNode;

    Ogre::BillboardSet* m_pPlasmaBillboardSet;
    Ogre::SceneNode* m_pPlasmaNode;

    Ogre::BillboardSet* m_pPhaserBillboardSet;
    Ogre::SceneNode* m_pPhaserNode;
};

